﻿#include "hzpch.h"
#include "Renderer2D.h"

#include "SourceCode/Renderer/Shader.h"
#include "SourceCode/Renderer/VertexArray.h"
#include "SourceCOde/Renderer/RenderCommand.h"
#include "SourceCode/Renderer/UniformBuffer.h"

namespace Hazal
{
	struct SQuadVertex
	{
		glm::vec3 m_vecPosition;
		glm::vec4 m_vecColor;
		glm::vec2 m_vecTexCoord;
		float m_fTexIndex;
		float m_fTilingFactor;

		/// Editor only
		int32_t m_nEntityID = -1;
	};

	struct SCircleVertex
	{
		glm::vec3 m_vecWorldPosition;
		glm::vec3 m_vecLocalPosition;
		glm::vec4 m_vecColor;
		float m_fThickness;
		float m_fFade;

		/// Editor only
		int32_t m_nEntityID = -1;
	};


	struct SRenderer2DData
	{
		static const uint32_t m_unMaxQuads = 500;					/// Max quads number can be generated
		static const uint32_t m_unMaxVertices = m_unMaxQuads * 4;	/// Max vertcies
		static const uint32_t m_unMaxIndices = m_unMaxQuads * 6;
		static const uint32_t m_unMaxTextureSlots = 32;


		/// Quad about
		uint32_t m_unQuadIndexCount = 0;

		Ref<CVertexArray> m_quadVertexArray;	///< VAO
		Ref<CVertexBuffer> m_quadVertexBuffer;	///< VBO
		Ref<CShader> m_quadShader;			///< Shader with texture & color
		Ref<CTexture2D> m_whiteTexture;			///< White texture

		SQuadVertex* m_pQuadVertexBufferBase = nullptr;
		SQuadVertex* m_pQuadVertexBufferPtr = nullptr;
		/// Quad about

		/// Circle about
		uint32_t m_unCircleIndexCount = 0;

		Ref<CVertexArray> m_circleVertexArray;	///< circle VAO
		Ref<CVertexBuffer> m_circleVertexBuffer;///< circle VBO
		Ref<CShader> m_circleShader;			///< circle shader

		SCircleVertex* m_pCircleVertexBufferBase = nullptr;
		SCircleVertex* m_pCircleVertexBufferPtr = nullptr;
		/// Circle about

		Ref<CUniformBuffer> m_cameraUniBuffer;	///< Camera uniform buffer object

		std::array<Ref<CTexture2D>, m_unMaxTextureSlots> m_aryTextureSlots;
		uint32_t m_unTextureSlotIdx = 1;		///< 0 表示纯色纹理, 真正加载的纹理索引从1开始

		glm::vec4 m_vecQuadVertexPos[4];		///< Model矩阵变换时需要的quad顶点

		CRenderer2D::SStatistics m_stats;		///< 渲染统计信息(渲染Quad的数量, 顶点数, 索引数)

		const glm::vec2 m_aTexCoords[4] = {		///< 默认纹理坐标
			{0.0f, 0.0f},
			{1.0f, 0.0f},
			{1.0f, 1.0f},
			{0.0f, 1.0f},
		};

		struct SCameraData
		{
			glm::mat4 m_matViewProjMatrix;
		};
		SCameraData m_cameraBuffer;

		~SRenderer2DData()
		{
			if (nullptr != m_pQuadVertexBufferBase)
			{
				delete[] m_pQuadVertexBufferBase;
				m_pQuadVertexBufferBase = nullptr;
			}
		}
	};

	static SRenderer2DData s_Data;

	void CRenderer2D::Init()
	{
		/// Render a quad 
		s_Data.m_quadVertexArray = CVertexArray::Create();
		s_Data.m_quadVertexArray->Bind();

		s_Data.m_quadVertexBuffer = CVertexBuffer::Create(SRenderer2DData::m_unMaxVertices * sizeof(SQuadVertex));
		{
			CBufferLayout layout = {
				SBufferElement(EShaderDataType::Float3, "a_Position"),
				SBufferElement(EShaderDataType::Float4, "a_Color"),
				SBufferElement(EShaderDataType::Float2, "a_TexCoord"),
				SBufferElement(EShaderDataType::Float, "a_TexIndex"),
				SBufferElement(EShaderDataType::Float, "a_TilingFactor"),
				SBufferElement(EShaderDataType::Int, "a_EntityID"),
			};

			s_Data.m_quadVertexBuffer->SetLayout(layout);
		}

		/// 初始化存放Vertex的可更改内存空间, 在指定渲染流程中将存储的数据传入GPU
		s_Data.m_pQuadVertexBufferBase = new SQuadVertex[SRenderer2DData::m_unMaxVertices];

		/// 存储每个矩形待绘制的顶点顺序至GPU
		uint32_t* pQuadIndices = new uint32_t[SRenderer2DData::m_unMaxIndices];
		uint32_t unOffset = 0;
		for (uint32_t i = 0; i < SRenderer2DData::m_unMaxIndices; i += 6)
		{
			pQuadIndices[i + 0] = unOffset + 0;
			pQuadIndices[i + 1] = unOffset + 1;
			pQuadIndices[i + 2] = unOffset + 2;

			pQuadIndices[i + 3] = unOffset + 2;
			pQuadIndices[i + 4] = unOffset + 3;
			pQuadIndices[i + 5] = unOffset + 0;

			/// 偏移量=构成Quad的顶点数
			unOffset += 4;
		}

		Ref<CIndexBuffer> quadIBO;
		quadIBO.reset(CIndexBuffer::Create(pQuadIndices, SRenderer2DData::m_unMaxIndices));

		if (nullptr != pQuadIndices)
		{
			delete[] pQuadIndices;
			pQuadIndices = nullptr;
		}

		s_Data.m_quadVertexArray->AddVertexBuffer(s_Data.m_quadVertexBuffer);
		s_Data.m_quadVertexArray->SetIndexBuffer(quadIBO);

		s_Data.m_quadVertexArray->Unbind();
		/// Render a quad 

		/// Render a circle
		s_Data.m_circleVertexArray = CVertexArray::Create();
		s_Data.m_circleVertexArray->Bind();

		s_Data.m_circleVertexBuffer = CVertexBuffer::Create(SRenderer2DData::m_unMaxVertices * sizeof(SCircleVertex));
		{
			CBufferLayout layout = {
				SBufferElement(EShaderDataType::Float3, "a_WorldPosition"	),
				SBufferElement(EShaderDataType::Float3, "a_LocalPosition"	),
				SBufferElement(EShaderDataType::Float4, "a_Color"			),
				SBufferElement(EShaderDataType::Float,	"a_Thickness"		),
				SBufferElement(EShaderDataType::Float,	"a_Fade"			),
				SBufferElement(EShaderDataType::Int,	"a_EntityID"		),
			};

			s_Data.m_circleVertexBuffer->SetLayout(layout);
		}
		s_Data.m_circleVertexArray->AddVertexBuffer(s_Data.m_circleVertexBuffer);
		s_Data.m_circleVertexArray->SetIndexBuffer(quadIBO);		/// 传入的顶点数与quad, 所以复用上面的IndexBuffer, 后续可能需要更改
		s_Data.m_pCircleVertexBufferBase = new SCircleVertex[SRenderer2DData::m_unMaxVertices];
		/// Render a circle


		/// Quad shader
		int32_t aSampler2D[s_Data.m_unMaxTextureSlots] = { 0 };
		for (uint32_t i = 0; i < s_Data.m_unMaxTextureSlots; ++i)
		{
			aSampler2D[i] = i;
		}

		s_Data.m_quadShader = CShader::Create("../../Assets/Shaders/Renderer2D_Quad.glsl");
		s_Data.m_circleShader = CShader::Create("../../Assets/Shaders/Renderer2D_Circle.glsl");

		/// Create camera uniform buffer
		s_Data.m_cameraUniBuffer = CUniformBuffer::Create(sizeof(SRenderer2DData::SCameraData), 0);

		/// White texture
		uint32_t unWhiteData = 0xFFFFFFFF;
		s_Data.m_whiteTexture = CTexture2D::Create(1, 1);
		s_Data.m_whiteTexture->SetData(&unWhiteData, sizeof(uint32_t));

		/// Init texture slot array
		s_Data.m_aryTextureSlots[0] = s_Data.m_whiteTexture;

		/// Init Quad vertex position
		s_Data.m_vecQuadVertexPos[0] = { -0.5f, -0.5f, 0.0f, 1.0f };
		s_Data.m_vecQuadVertexPos[1] = {  0.5f, -0.5f, 0.0f, 1.0f };
		s_Data.m_vecQuadVertexPos[2] = {  0.5f,  0.5f, 0.0f, 1.0f };
		s_Data.m_vecQuadVertexPos[3] = { -0.5f,  0.5f, 0.0f, 1.0f };
	}

	void CRenderer2D::Shutdown()
	{

	}

	void CRenderer2D::BeginScene(const CCamera& camera, const glm::mat4& matTransform)
	{
		s_Data.m_cameraBuffer.m_matViewProjMatrix = camera.GetProjection() * glm::inverse(matTransform);

		s_Data.m_quadShader->Bind();
		s_Data.m_cameraUniBuffer->SetData(&s_Data.m_cameraBuffer, sizeof(SRenderer2DData::SCameraData));

		StartBatch();
	}

	void CRenderer2D::BeginScene(const CEditorCamera& camera)
	{
		s_Data.m_quadShader->Bind();

		s_Data.m_cameraBuffer.m_matViewProjMatrix = camera.GetViewProjection();
		s_Data.m_cameraUniBuffer->SetData(&s_Data.m_cameraBuffer, sizeof(SRenderer2DData::SCameraData));

		StartBatch();
	}

	void CRenderer2D::BeginScene(const COrthoGraphicCamera& camera)
	{
		s_Data.m_quadShader->Bind();

		s_Data.m_cameraBuffer.m_matViewProjMatrix = camera.GetViewProjMatrix();
		s_Data.m_cameraUniBuffer->SetData(&s_Data.m_cameraBuffer, sizeof(SRenderer2DData::SCameraData));

		StartBatch();
	}

	void CRenderer2D::EndScene()
	{
		Flush();
	}

	void CRenderer2D::FlushAndReset()
	{
		CRenderer2D::EndScene();

		StartBatch();
	}

	void CRenderer2D::StartBatch()
	{
		s_Data.m_unQuadIndexCount = 0;
		s_Data.m_pQuadVertexBufferPtr = s_Data.m_pQuadVertexBufferBase;

		s_Data.m_unCircleIndexCount = 0;
		s_Data.m_pCircleVertexBufferPtr = s_Data.m_pCircleVertexBufferBase;

		s_Data.m_unTextureSlotIdx = 1;
	}

	void CRenderer2D::Flush()
	{
		if (s_Data.m_unQuadIndexCount)
		{
			uint32_t unDataSize = (uint8_t*)s_Data.m_pQuadVertexBufferPtr - (uint8_t*)s_Data.m_pQuadVertexBufferBase;
			s_Data.m_quadVertexBuffer->SetData(s_Data.m_pQuadVertexBufferBase, unDataSize);

			/// Bind textures
			for (uint32_t i = 0; i < s_Data.m_unTextureSlotIdx; ++i)
			{
				s_Data.m_aryTextureSlots[i]->Bind(i);
			}

			CRenderCommand::DrawIndexed(s_Data.m_quadVertexArray, s_Data.m_unQuadIndexCount);

			s_Data.m_stats.m_unDrawCall++;
		}

		if (s_Data.m_unCircleIndexCount)
		{
			uint32_t unDataSize = (uint8_t*)s_Data.m_pCircleVertexBufferPtr - (uint8_t*)s_Data.m_pCircleVertexBufferBase;
			s_Data.m_quadVertexBuffer->SetData(s_Data.m_pCircleVertexBufferPtr, unDataSize);
			
			s_Data.m_circleShader->Bind();

			CRenderCommand::DrawIndexed(s_Data.m_circleVertexArray, s_Data.m_unCircleIndexCount);
			s_Data.m_stats.m_unDrawCall++;
		}
	}

	/* ========== Quad without texture ========== */
	void CRenderer2D::DrawQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, const glm::vec4& vecColor)
	{
		DrawQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, vecColor);
	}

	void CRenderer2D::DrawQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, const glm::vec4& vecColor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });

		float fTextureIdx = 0.0f;
		float fTilingFactor = 1.0f;

		DrawVertices(matTransform, vecColor, fTextureIdx);
	}

	void CRenderer2D::DrawQuad(const glm::mat4& matTransform, const glm::vec4& vecColor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		float fTextureIdx = 0.0f;
		float fTilingFactor = 1.0f;

		DrawVertices(matTransform, vecColor, fTextureIdx);
	}
	/* ========================================== */

	/* =========== Quad with texture =========== */
	void CRenderer2D::DrawQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, const Ref<CTexture2D>& rTexture, float fTilingFactor)
	{
		DrawQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, rTexture, fTilingFactor);
	}

	void CRenderer2D::DrawQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, const Ref<CTexture2D>& rTexture, float fTilingFactor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		float fTextureIdx = 0.0f;
		for (uint32_t i = 1; i < s_Data.m_unTextureSlotIdx; ++i)
		{
			if (*s_Data.m_aryTextureSlots[i].get() == *rTexture.get())
			{
				fTextureIdx = (float)i;
				break;
			}
		}

		if (0.0f == fTextureIdx)
		{
			fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
			s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = rTexture;
		}

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });

		constexpr glm::vec4 vecColor = { 1.0f, 1.0f, 1.0f, 1.0f };

		DrawVertices(matTransform, vecColor, fTextureIdx, nullptr, fTilingFactor);

	}
	void CRenderer2D::DrawQuad(const glm::mat4& matTransform, const glm::vec4& vecColor, const Ref<CTexture2D>& rTexture, float fTilingFactor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		float fTextureIdx = 0.0f;
		for (uint32_t i = 1; i < s_Data.m_unTextureSlotIdx; ++i)
		{
			if (*s_Data.m_aryTextureSlots[i].get() == *rTexture.get())
			{
				fTextureIdx = (float)i;
				break;
			}
		}

		if (0.0f == fTextureIdx)
		{
			fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
			s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = rTexture;
		}

		DrawVertices(matTransform, vecColor, fTextureIdx, nullptr, fTilingFactor);
	}

	/* ========================================== */

	/* =========== Quad with sub texture =========== */
	void CRenderer2D::DrawQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, const Ref<CSubTexture2D>& rSubTexture, float fTilingFactor)
	{
		DrawQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, rSubTexture, fTilingFactor);
	}

	void CRenderer2D::DrawQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, const Ref<CSubTexture2D>& rSubTexture, float fTilingFactor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		Ref<CTexture2D> texture = rSubTexture->GetTexture();

		float fTextureIdx = 0.0f;
		for (uint32_t i = 0; i < s_Data.m_unTextureSlotIdx; ++i)
		{
			if (*s_Data.m_aryTextureSlots[i].get() == *texture.get())
			{
				fTextureIdx = (float)i;
				break;
			}
		}

		if (0.0f == fTextureIdx)
		{
			fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
			s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = texture;
		}

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });
		
		constexpr glm::vec4 vecColor = { 1.0f, 1.0f, 1.0f, 1.0f };

		DrawVertices(matTransform, vecColor, fTextureIdx, rSubTexture->GetTexCoord(), fTilingFactor);

	}
	/* ============================================= */


	/* ========== Rotate Quad without texture ========== */
	void CRenderer2D::DrawRotateQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, float fRotation, const glm::vec4& vecColor)
	{
		DrawRotateQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, fRotation, vecColor);
	}

	void CRenderer2D::DrawRotateQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, float fRotation, const glm::vec4& vecColor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		float fTextureIdx = 0.0f;
		float fTilingFactor = 1.0f;

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::rotate(glm::mat4(1.0f), glm::radians(fRotation), { 0.0f, 0.0f, 1.0f })
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });

		DrawVertices(matTransform, vecColor, fTextureIdx);
	}
	/* ================================================ */

	/* ========== Rotate Quad with texture ========== */
	void CRenderer2D::DrawRotateQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, float fRotation, const Ref<CTexture2D>& rTexture, float fTilingFactor)
	{
		DrawRotateQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, fRotation, rTexture, fTilingFactor);
	}

	void CRenderer2D::DrawRotateQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, float fRotation, const Ref<CTexture2D>& rTexture, float fTilingFactor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		float fTextureIdx = 0.0f;
		for (uint32_t i = 1; i < s_Data.m_unTextureSlotIdx; ++i)
		{
			if (*s_Data.m_aryTextureSlots[i].get() == *rTexture.get())
			{
				fTextureIdx = (float)i;
				break;
			}
		}

		if (0.0f == fTextureIdx)
		{
			fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
			s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = rTexture;
		}

		constexpr glm::vec4 vecColor = { 1.0f, 1.0f, 1.0f, 1.0f };

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::rotate(glm::mat4(1.0f), glm::radians(fRotation), { 0.0f, 0.0f, 1.0f })
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });

		DrawVertices(matTransform, vecColor, fTextureIdx, nullptr, fTilingFactor);
	}
	/* ============================================== */

	/* ========== Rotate Quad with sub texture ========== */
	void CRenderer2D::DrawRotateQuad(const glm::vec2& vecPos, const glm::vec2& vecSize, float fRotation, const Ref<CSubTexture2D>& rSubTexture, float fTilingFactor)
	{
		DrawRotateQuad({ vecPos.x, vecPos.y, 0.0f }, vecSize, fRotation, rSubTexture, fTilingFactor);
	}
	
	void CRenderer2D::DrawRotateQuad(const glm::vec3& vecPos, const glm::vec2& vecSize, float fRotation, const Ref<CSubTexture2D>& rSubTexture, float fTilingFactor)
	{
		HZ_PROFILE_FUNCTION();

		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		Ref<CTexture2D> texture = rSubTexture->GetTexture();

		float fTextureIdx = 0.0f;
		for (uint32_t i = 1; i < s_Data.m_unTextureSlotIdx; ++i)
		{
			if (*s_Data.m_aryTextureSlots[i].get() == *texture.get())
			{
				fTextureIdx = (float)i;
				break;
			}
		}

		if (0.0f == fTextureIdx)
		{
			fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
			s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = texture;
		}

		constexpr glm::vec4 vecColor = { 1.0f, 1.0f, 1.0f, 1.0f };

		glm::mat4 matTransform = glm::translate(glm::mat4(1.0f), vecPos)
			* glm::rotate(glm::mat4(1.0f), glm::radians(fRotation), { 0.0f, 0.0f, 1.0f })
			* glm::scale(glm::mat4(1.0f), { vecSize.x, vecSize.y, 1.0f });

		DrawVertices(matTransform, vecColor, fTextureIdx, rSubTexture->GetTexCoord(), fTilingFactor);
	}
	/* ================================================== */

	void CRenderer2D::DrawSprite(const glm::mat4& matTransform, SSpriteRendererComponent& spriteComponent, int nEntityID)
	{
		if (s_Data.m_unQuadIndexCount >= SRenderer2DData::m_unMaxIndices)
		{
			FlushAndReset();
		}

		const glm::vec2* pCurVecTexCoord = s_Data.m_aTexCoords;
		const glm::vec4& vecColor = spriteComponent.m_vecColor;

		float fTextureIdx = 0.0f;
		float fTilingFactor = spriteComponent.m_fTilingfFactor;

		if (spriteComponent.m_texture)
		{
			for (uint32_t i = 1; i < s_Data.m_unTextureSlotIdx; ++i)
			{
				if (*s_Data.m_aryTextureSlots[i].get() == *spriteComponent.m_texture.get())
				{
					fTextureIdx = (float)i;
					break;
				}
			}

			if (0.0f == fTextureIdx)
			{
				fTextureIdx = (float)s_Data.m_unTextureSlotIdx;
				s_Data.m_aryTextureSlots[s_Data.m_unTextureSlotIdx++] = spriteComponent.m_texture;
			}
		}

		constexpr uint32_t unQuadVertexCount = 4;
		for (uint32_t i = 0; i < unQuadVertexCount; ++i)
		{
			s_Data.m_pQuadVertexBufferPtr->m_vecPosition = matTransform * s_Data.m_vecQuadVertexPos[i];
			s_Data.m_pQuadVertexBufferPtr->m_vecColor = vecColor;
			s_Data.m_pQuadVertexBufferPtr->m_vecTexCoord = pCurVecTexCoord[i];
			s_Data.m_pQuadVertexBufferPtr->m_fTexIndex = fTextureIdx;
			s_Data.m_pQuadVertexBufferPtr->m_fTilingFactor = fTilingFactor;
			s_Data.m_pQuadVertexBufferPtr->m_nEntityID = nEntityID;
			s_Data.m_pQuadVertexBufferPtr++;
		}
		
		s_Data.m_unQuadIndexCount += 6;

		s_Data.m_stats.m_unQuadCount++;
	}

	void CRenderer2D::ResetStats()
	{
		memset(&s_Data.m_stats, 0, sizeof(SStatistics));
	}

	CRenderer2D::SStatistics& CRenderer2D::GetStats()
	{
		return s_Data.m_stats;
	}

	void CRenderer2D::DrawVertices(const glm::mat4& matTransform, const glm::vec4& vecColor, float fTextureIdx, const glm::vec2* pVecTexCoord,  float fTilingFactor)
	{
		const glm::vec2* pCurVecTexCoord;
		pCurVecTexCoord = pVecTexCoord;

		if (nullptr == pVecTexCoord)
		{
			pCurVecTexCoord = s_Data.m_aTexCoords;
		}

		constexpr uint32_t unQuadVertexCount = 4;
		for (uint32_t i = 0; i < unQuadVertexCount; ++i)
		{
			s_Data.m_pQuadVertexBufferPtr->m_vecPosition = matTransform * s_Data.m_vecQuadVertexPos[i];
			s_Data.m_pQuadVertexBufferPtr->m_vecColor = vecColor;
			s_Data.m_pQuadVertexBufferPtr->m_vecTexCoord = pCurVecTexCoord[i];
			s_Data.m_pQuadVertexBufferPtr->m_fTexIndex = fTextureIdx;
			s_Data.m_pQuadVertexBufferPtr->m_fTilingFactor = fTilingFactor;
			s_Data.m_pQuadVertexBufferPtr++;
		}

		s_Data.m_unQuadIndexCount += 6;

		s_Data.m_stats.m_unQuadCount++;
	}

	/* ================== Rotate Circle ================= */
	void CRenderer2D::DrawCircle(const glm::mat4& matTransform, const glm::vec4& vecColor, float fThickness, float fFade, int nEntityID)
	{
		HZ_PROFILE_FUNCTION();

		/// TODO: implements for circles

		for (size_t i = 0; i < 4; ++i)
		{
			s_Data.m_pCircleVertexBufferPtr->m_vecWorldPosition = matTransform * s_Data.m_vecQuadVertexPos[i];
			s_Data.m_pCircleVertexBufferPtr->m_vecLocalPosition = s_Data.m_vecQuadVertexPos[i] * 2.0f;
			s_Data.m_pCircleVertexBufferPtr->m_vecColor = vecColor;
			s_Data.m_pCircleVertexBufferPtr->m_fThickness = fThickness;
			s_Data.m_pCircleVertexBufferPtr->m_fFade = fFade;
			s_Data.m_pCircleVertexBufferPtr->m_nEntityID = nEntityID;
			s_Data.m_pCircleVertexBufferPtr++;
		}

		s_Data.m_unCircleIndexCount += 6;

		/// TODO: implements for circles
		s_Data.m_stats.m_unQuadCount++;

	}
	/* ================================================== */
}